using System;
using System.Collections;
using System.Collections.Generic;
using System.Html;
using System.Runtime.CompilerServices;
using Awaiter;
using Nsb.Classes;
using ThemeChain;
using jQueryApi;

namespace Actors
{
    [IgnoreNamespace]
    public class EyesPoint
    {
        public int EyesLeft;
        public int EyesTop;
        public int LeftPupilLeft;
        public int LeftPupilTop;
        public int RightPupilLeft;
        public int RightPupilTop;
        public int X;
        public int Y;
        public bool Followable;

        public EyesPoint(int x, int y)
        {
            X = x;
            Y = y;
        }

        public EyesPoint Collect(Eyes eyes)
        {
            jQueryPosition p = eyes.EyeBalls.GetOffset();
            EyesTop = p.Top;
            EyesLeft = p.Left;
            p = eyes.Pupil1.Position();
            LeftPupilTop = p.Top;
            LeftPupilLeft = p.Left;
            p = eyes.Pupil2.Position();
            RightPupilTop = p.Top;
            RightPupilLeft = p.Left - 34;
            Followable = eyes.IsFollowable;
            return this;
        }

        public bool Compare(EyesPoint p)
        {
            return
                LeftPupilLeft == p.LeftPupilLeft &&
                LeftPupilTop == p.LeftPupilTop &&
                RightPupilLeft == p.RightPupilLeft &&
                RightPupilTop == p.RightPupilTop &&
                EyesLeft == p.EyesLeft &&
                EyesTop == p.EyesTop &&
                X == p.X &&
                Y == p.Y
                ;
        }
    }

    [IgnoreNamespace]
    public class Eyes : Actor
    {
        public int MarginBottom = 8;
        public double ElementHorizontal = .666;
        private const double Dv = 1.7;
        public readonly jQueryObject EyeBalls;

        private readonly EyesPoint _here = new EyesPoint(0, 0);
        private readonly List<EyesPoint> _pointStack = new List<EyesPoint>();
        public readonly jQueryObject Pupil1;
        public readonly jQueryObject Pupil2;
        private double _angle1;
        private double _angle2;
        private int _d1;
        private int _d2;
        private bool _doFlirting;
        private double _fx;
        private double _fy;

        public Eyes()
        {
            Element = jQuery.FromHtml("<div style='position:absolute;top:0px;left:0px;width:69px;height:34px;display:none'/>");
            EyeBalls = jQuery.FromHtml("<div style='position:absolute;top:0px;left:0px;width:69px;height:34px;'/>").AppendTo(Element);
            Pupil1 = jQuery.FromHtml("<div style='position:absolute;top:13px;left:13px;width:12px;height:13px;'/>").AppendTo(EyeBalls);
            Pupil2 = jQuery.FromHtml("<div style='position:absolute;top:13px;left:26px;width:12px;height:13px;'/>").AppendTo(EyeBalls);

            EyeBalls.Append(jQuery.FromHtml("<img src='" + Uri.Img("Eyes/eye2.gif") + "' width='69' height='34'/>"));
            Pupil1.Append(jQuery.FromHtml("<img src='" + Uri.Img("Eyes/pupils.gif") + "' width='13' height='13'/>"));
            Pupil2.Append(jQuery.FromHtml("<img src='" + Uri.Img("Eyes/pupils.gif") + "' width='13' height='13'/>"));

            FadeIn = 900;
            FadeOut = 500;

            //Window.SetInterval(Stare, 3000);
        }

        [AlternateSignature]
        public static extern Eyes BodyEyes();

        public static Eyes BodyEyes(bool flirting)
        {
            Eyes eyes = new Eyes().Flirting(!Script.IsValue(flirting) || flirting);
            eyes.Element.AppendTo(Document.Body);
            return eyes;
        }

        #region Public functions

        public Eyes MoveTo(int x, int y)
        {
            new Await()
                .AddDx(delegate
                       {
                           UnfollowMouse();
                           MouseY = _here.Y = y;
                           MouseX = _here.X = x;
                           StartActing();
                       })
                .AddAw(WaitTrackingDoneAw)
                .AddFn(Flirt)
                .Commit();
            return this;
        }

        public Eyes MoveToElement(jQueryObject el)
        {
            return MoveTo(el.GetOffset().Left + Number.ParseInt(el.GetWidth() * ElementHorizontal), el.GetOffset().Top + MarginBottom);
        }

        public Eyes HideEyes()
        {
            Acting = false;
            Window.SetTimeout(
                delegate
                {
                    if (!Acting)
                    {
                        UnfollowMouse();
                        MoveTo(Number.ParseInt(Math.Random() * Window.InnerWidth), Number.ParseInt(Math.Random() * Window.InnerHeight));
                        Element.FadeOut(FadeOut);
                    }
                }, 200);
            return this;
        }

        public void Flirt()
        {
            if (_doFlirting)
                Stare(350);
        }

        public Eyes Flirting(bool ic)
        {
            _doFlirting = ic;
            return this;
        }

        public void Stare(int ms)
        {
            new Await()
                .AddFn(PushEyes)
                .AddDx(delegate
                       {
                           MovePupil(Pupil1, 13, 13, 200);
                           MovePupil(Pupil2, 9, 13, 200);
                       })
                .AddAw(WaitTrackingDoneAw)
                .Sleep(ms)
                .AddFn(PopPupils, 145)
                .Commit();
        }

        public void Destroy()
        {
            Element.Remove();
            ResetTimers();
        }

        #endregion Public Functions

        #region Private Functions

        private readonly EyesPoint _afterPt = new EyesPoint(0, 0);
        private readonly EyesPoint _beforePt = new EyesPoint(0, 0);

        private void MovePupil(jQueryObject pupil, int x, int y, int ms)
        {
            IsFollowable = false;
            TrackingDone = false;
            if (pupil == Pupil2)
                x += 34;
            pupil.Animate(new Dictionary("top", y + "px", "left", x + "px"), ms, EffectEasing.Swing, delegate { TrackingDone = true; });
        }

        private void PushEyes()
        {
            EyesPoint pp = new EyesPoint(_here.X, _here.Y).Collect(this);
            _pointStack.Add(pp);
        }

        private void PopPupils(int ms)
        {
            if (_pointStack.Count > 0)
            {
                EyesPoint pp = _pointStack[_pointStack.Count - 1];
                _pointStack.RemoveAt(_pointStack.Count - 1);
                if (FollowDelegate == null)
                {
                    MovePupil(Pupil1, pp.LeftPupilLeft, pp.LeftPupilTop, ms);
                    MovePupil(Pupil2, pp.RightPupilLeft, pp.RightPupilTop, ms);
                }
                IsFollowable = pp.Followable;
            }
        }

        protected override void Follow()
        {
            Window.ClearTimeout(FollowTmr);
            _fy += (MouseY - _fy) * 0.12 - 2;
            _fx += (MouseX - _fx) * 0.12;
            _beforePt.Collect(this);
            Track();
            _afterPt.Collect(this);
            TrackingDone = _beforePt.Compare(_afterPt);
            FollowTmr = Window.SetTimeout(Follow, 30);
        }

        private void Track()
        {
            //int sy = FollowDelegate != null ? Window.PageYOffset : 0;
            int sy = 0;
            int wy = Window.InnerHeight + DocumentScrollTop();
            int wx = Window.InnerWidth;

            //Keep eyes on screen.
            double chy = Math.Floor(_fy - 34);
            if (chy <= 0) chy = 0;
            if (chy >= wy - 34) chy = wy - 34;
            double chx = Math.Floor(_fx - 34);
            if (chx <= 0) chx = 0;
            if (chx >= wx - 69) chx = wx - 69;

            int ebTop = (int)(chy + sy);
            int ebLeft = (int)chx;

            EyeBalls.CSS("top", ebTop + "px");
            EyeBalls.CSS("left", ebLeft + "px");

            if (!IsFollowable)
                return;

            //eyeball1 centre.
            int c1Y = Number.ParseInt(ebTop) + 17;
            int c1X = Number.ParseInt(ebLeft) + 17;

            //eyeball2 centre.
            int c2Y = Number.ParseInt(ebTop) + 17;
            int c2X = Number.ParseInt(ebLeft) + 52;

            int dy1 = MouseY + sy - c1Y;
            int dx1 = MouseX - c1X;
            _d1 = Math.Sqrt(dy1 * dy1 + dx1 * dx1);
            int dy2 = MouseY + sy - c2Y;
            int dx2 = MouseX - c2X;
            _d2 = Math.Sqrt(dy2 * dy2 + dx2 * dx2);
            int ay1 = MouseY + sy - c1Y;
            int ax1 = MouseX - c1X;
            _angle1 = Math.Atan2(ay1, ax1) * 180 / Math.PI;
            int ay2 = MouseY + sy - c2Y;
            int ax2 = MouseX - c2X;
            _angle2 = Math.Atan2(ay2, ax2) * 180 / Math.PI;

            jQueryPosition p = EyeBalls.GetOffset();

            int p1T = (int)((_d1 < 17) ? (c1Y - 6 + _d1 / Dv * Math.Sin(_angle1 * Math.PI / 180)) : (c1Y - 6 + 10 * Math.Sin(_angle1 * Math.PI / 180)));
            int p1L = (int)((_d1 < 17) ? (c1X - 6 + _d1 / Dv * Math.Cos(_angle1 * Math.PI / 180)) : (c1X - 6 + 10 * Math.Cos(_angle1 * Math.PI / 180)));
            int p2T = (int)((_d2 < 17) ? (c2Y - 6 + _d2 / Dv * Math.Sin(_angle2 * Math.PI / 180)) : (c2Y - 6 + 10 * Math.Sin(_angle2 * Math.PI / 180)));
            int p2L = (int)((_d2 < 17) ? (c2X - 6 + _d2 / Dv * Math.Cos(_angle2 * Math.PI / 180)) : (c2X - 6 + 10 * Math.Cos(_angle2 * Math.PI / 180)));

            Pupil1.CSS("top", p1T - p.Top + "px");
            Pupil1.CSS("left", p1L - p.Left + "px");
            Pupil2.CSS("top", p2T - p.Top + "px");
            Pupil2.CSS("left", p2L - p.Left + "px");
        }

        #endregion Private Functions
    }
}